package cn.thornbird.orgsync.util;

import cn.thornbird.orgsync.exception.BadRequestException;
import cn.thornbird.orgsync.exception.InternalServerErrorException;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Iterator;

@Slf4j
public class ImageUtil {

    private static final String[] IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png"};

    public static void cropImage(String url, int width, int height)
            throws BadRequestException, InternalServerErrorException {
        String srcPath = url;
        srcPath = srcPath.replaceAll("/", File.separator);
        File srcFile = new File(srcPath);
        if (!srcFile.exists()) {
            throw new BadRequestException("Non-existent file");
        }

        boolean flag = false;
        String lowerCaseSrcPath = srcPath.toLowerCase();
        for (String extension : IMAGE_EXTENSIONS) {
            if (lowerCaseSrcPath.endsWith(extension.toLowerCase())) {
                flag = true;
            }
        }
        if (!flag) {
            throw new BadRequestException("Incorrect image format");
        }

        if (width <= 0 || height <= 0) {
            throw new BadRequestException("Incorrect image size");
        }

        String extension = srcPath.substring(srcPath.lastIndexOf(".") + 1).toLowerCase();
        String filename = EncryptUtil.generateGUID() + "." + extension;
        String[] parts = srcPath.split(File.separator);
        parts[parts.length - 1] = filename;
        String destPath = String.join(File.separator, parts);
        File destFile = new File(destPath);
        String formatName = extension.equals("jpg") ? "jpeg" : extension;
        try {
            cropImage(srcFile, destFile, width, height, formatName);
        } catch (IOException e) {
            log.error("error", e);
            throw new InternalServerErrorException();
        }

        try {
            Files.copy(destFile.toPath(), srcFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            destFile.delete();
        } catch (IOException e) {
            log.error("error", e);
            throw new InternalServerErrorException();
        }
    }

    private static void cropImage(File srcFile, File destFile, int width, int height, String formatName)
            throws IOException {
        Iterator<ImageReader> iterator = ImageIO.getImageReadersByFormatName(formatName);
        ImageReader reader = iterator.next();
        ImageInputStream iis = ImageIO.createImageInputStream(new FileInputStream(srcFile));
        reader.setInput(iis, true);
        ImageReadParam param = reader.getDefaultReadParam();
        int srcWidth = reader.getWidth(0);
        int srcHeight = reader.getHeight(0);
        int destWidth = srcWidth;
        int destHeight = srcHeight;
        int x = 0;
        int y = 0;
        float srcRatio = reader.getAspectRatio(0);
        float destRatio = (float) width / height;
        if (destRatio >= srcRatio) {
            destHeight = Math.round(srcWidth / destRatio);
            y = Math.round((srcHeight - destHeight) * 0.5f);
        } else {
            destWidth = Math.round(srcHeight * destRatio);
            x = Math.round((srcWidth - destWidth) * 0.5f);
        }
        Rectangle rectangle = new Rectangle(x, y, destWidth, destHeight);
        param.setSourceRegion(rectangle);
        BufferedImage bi;
        if (rectangle.getWidth() <= width) {
            bi = reader.read(0, param);
        } else {
            BufferedImage temp = reader.read(0, param);
            bi = new BufferedImage(width, height, temp.getType());
            bi.getGraphics().drawImage(temp, 0, 0, width, height, null);
        }
        ImageIO.write(bi, formatName, destFile);
    }

}
